#include <stdio.h>
#include "Game.h"
#include "TGA.h"
#ifdef WIN32
#include "OpenGLExt.h"
#else
#include <GLES2/gl2.h>
#include <GLES2/gl2ext.h>
#endif
#ifdef WIN32
#define  LOGI printf
#else
#include <android/log.h>

#define  LOG_TAG    "libgl2jni"
#define  LOGI(...)  __android_log_print(ANDROID_LOG_INFO,LOG_TAG,__VA_ARGS__)
#endif
Game* Game::instance = 0;
void Game::CreateInstance()
{
	if(instance == 0)
	{
		instance = new Game();
	}
}
Game* Game::GetInstance()
{
	return instance;
}
void Game::Init(int width, int height)
{
	LOGI("hieu.dotri init %d x %d",width,height);
	iScreenWidth = width;
	iScreenHeight = height;
	glViewport(0,0,width,height);
	//mxProjection.SetOrtho(-width/2,width/2,-height/2,height/2,0.1,1000);
	mxProjection.SetPerspective(0.4,float(width)/float(height),0.1,1000);
	FILE *f = fopen("sdcard/vs.txt","rb");
	if(f)
	{
		LOGI("hieu.dotri open vs.txt");
		vs = glCreateShader(GL_VERTEX_SHADER);
		GLchar *shaderSrc;
		fseek(f,0,SEEK_END);
		GLint size = ftell(f);
		shaderSrc = new GLchar[size + 1];
		fseek(f,0,SEEK_SET);
		fread(shaderSrc,1,size,f);
		shaderSrc[size] = '\0';
		fclose(f);
		glShaderSource(vs,1,(const char **)&shaderSrc,0);
		glCompileShader(vs);
		delete[] shaderSrc;
	}
	else
	{
		LOGI("hieu.dotri can not open vs.txt");
	}
	f = fopen("sdcard/fs.txt","rb");
	if(f)
	{
		LOGI("hieu.dotri open fs.txt");
		fs = glCreateShader(GL_FRAGMENT_SHADER);
		GLchar *shaderSrc;
		fseek(f,0,SEEK_END);
		GLint size = ftell(f);
		shaderSrc = new GLchar[size + 1];
		fseek(f,0,SEEK_SET);
		fread(shaderSrc,1,size,f);
		shaderSrc[size] = '\0';
		fclose(f);
		glShaderSource(fs,1,(const char **)&shaderSrc,0);
		glCompileShader(fs);
		delete[] shaderSrc;
	}
	else
	{
		LOGI("hieu.dotri can not open fs.txt");
	}
	prg = glCreateProgram();
	glAttachShader(prg,vs);
	glAttachShader(prg,fs);
	glLinkProgram(prg);
	mvMatrix = glGetUniformLocation(prg,"u_mvMatrix");
	pMatrix = glGetUniformLocation(prg,"u_pMatrix");
	uniformLightPosition = glGetUniformLocation(prg,"lightPosition");
	uniformLightDiffuse = glGetUniformLocation(prg,"lightDiffuse");
	uniformAmbient = glGetUniformLocation(prg,"ambient");
	uniformTexture = glGetUniformLocation(prg,"texture1");
	posAttribute = glGetAttribLocation(prg,"a_posL");
	normalAttribute = glGetAttribLocation(prg,"a_normal");
	texCoordAttribute = glGetAttribLocation(prg,"a_texCoord");

	glGenBuffers(1, &vboId);
	GLfloat size = 10.0f;
	GLfloat LeftTopFront[3] = {-size,size,size};
	GLfloat RightTopFront[3] = {size,size,size};
	GLfloat RightBottomFront[3] = {size,-size,size};
	GLfloat LeftBottomFront[3] = {-size,-size,size};
	GLfloat LeftTopBack[3] = {-size,size,-size};
	GLfloat RightTopBack[3] = {size,size,-size};
	GLfloat RightBottomBack[3] = {size,-size,-size};
	GLfloat LeftBottomBack[3] = {-size,-size,-size};
	GLfloat normalFront[3] = {0.0,0.0,1.0};
	GLfloat normalBack[3] = {0.0,0.0,-1.0};
	GLfloat normalLeft[3] = {-1.0,0.0,0.0};
	GLfloat normalRight[3] = {1.0,0.0,0.0};
	GLfloat normalTop[3] = {0.0,1.0,0.0};
	GLfloat normalBottom[3] = {0.0,-1.0,0.0};
	GLfloat cubeVertices[] = 
	{
		LeftTopFront[0],LeftTopFront[1],LeftTopFront[2],0.0,0.0,normalFront[0],normalFront[1],normalFront[2],
		RightBottomFront[0],RightBottomFront[1],RightBottomFront[2],1.0,1.0,normalFront[0],normalFront[1],normalFront[2],
		RightTopFront[0],RightTopFront[1],RightTopFront[2],1.0,0.0,normalFront[0],normalFront[1],normalFront[2],
		LeftTopFront[0],LeftTopFront[1],LeftTopFront[2],0.0,0.0,normalFront[0],normalFront[1],normalFront[2],
		LeftBottomFront[0],LeftBottomFront[1],LeftBottomFront[2],0.0,1.0,normalFront[0],normalFront[1],normalFront[2],
		RightBottomFront[0],RightBottomFront[1],RightBottomFront[2],1.0,1.0,normalFront[0],normalFront[1],normalFront[2],

		RightTopFront[0],RightTopFront[1],RightTopFront[2],0.0,0.0,normalRight[0],normalRight[1],normalRight[2],
		RightBottomBack[0],RightBottomBack[1],RightBottomBack[2],1.0,1.0,normalRight[0],normalRight[1],normalRight[2],
		RightTopBack[0],RightTopBack[1],RightTopBack[2],1.0,0.0,normalRight[0],normalRight[1],normalRight[2],
		RightTopFront[0],RightTopFront[1],RightTopFront[2],0.0,0.0,normalRight[0],normalRight[1],normalRight[2],
		RightBottomFront[0],RightBottomFront[1],RightBottomFront[2],0.0,1.0,normalRight[0],normalRight[1],normalRight[2],
		RightBottomBack[0],RightBottomBack[1],RightBottomBack[2],1.0,1.0,normalRight[0],normalRight[1],normalRight[2],

		RightTopBack[0],RightTopBack[1],RightTopBack[2],0.0,0.0,normalBack[0],normalBack[1],normalBack[2],
		LeftBottomBack[0],LeftBottomBack[1],LeftBottomBack[2],1.0,1.0,normalBack[0],normalBack[1],normalBack[2],
		LeftTopBack[0],LeftTopBack[1],LeftTopBack[2],1.0,0.0,normalBack[0],normalBack[1],normalBack[2],
		RightTopBack[0],RightTopBack[1],RightTopBack[2],0.0,0.0,normalBack[0],normalBack[1],normalBack[2],
		RightBottomBack[0],RightBottomBack[1],RightBottomBack[2],0.0,1.0,normalBack[0],normalBack[1],normalBack[2],
		LeftBottomBack[0],LeftBottomBack[1],LeftBottomBack[2],1.0,1.0,normalBack[0],normalBack[1],normalBack[2],

		LeftTopBack[0],LeftTopBack[1],LeftTopBack[2],0.0,0.0,normalLeft[0],normalLeft[1],normalLeft[2],
		LeftBottomFront[0],LeftBottomFront[1],LeftBottomFront[2],1.0,1.0,normalLeft[0],normalLeft[1],normalLeft[2],
		LeftTopFront[0],LeftTopFront[1],LeftTopFront[2],1.0,0.0,normalLeft[0],normalLeft[1],normalLeft[2],
		LeftTopBack[0],LeftTopBack[1],LeftTopBack[2],0.0,0.0,normalLeft[0],normalLeft[1],normalLeft[2],
		LeftBottomBack[0],LeftBottomBack[1],LeftBottomBack[2],0.0,1.0,normalLeft[0],normalLeft[1],normalLeft[2],
		LeftBottomFront[0],LeftBottomFront[1],LeftBottomFront[2],1.0,1.0,normalLeft[0],normalLeft[1],normalLeft[2],

		LeftTopBack[0],LeftTopBack[1],LeftTopBack[2],0.0,0.0,normalTop[0],normalTop[1],normalTop[2],
		RightTopFront[0],RightTopFront[1],RightTopFront[2],1.0,1.0,normalTop[0],normalTop[1],normalTop[2],
		RightTopBack[0],RightTopBack[1],RightTopBack[2],1.0,0.0,normalTop[0],normalTop[1],normalTop[2],
		LeftTopBack[0],LeftTopBack[1],LeftTopBack[2],0.0,0.0,normalTop[0],normalTop[1],normalTop[2],
		LeftTopFront[0],LeftTopFront[1],LeftTopFront[2],0.0,1.0,normalTop[0],normalTop[1],normalTop[2],
		RightTopFront[0],RightTopFront[1],RightTopFront[2],1.0,1.0,normalTop[0],normalTop[1],normalTop[2],

		LeftBottomFront[0],LeftBottomFront[1],LeftBottomFront[2],0.0,0.0,normalBottom[0],normalBottom[1],normalBottom[2],
		RightBottomBack[0],RightBottomBack[1],RightBottomBack[2],1.0,1.0,normalBottom[0],normalBottom[1],normalBottom[2],
		RightBottomFront[0],RightBottomFront[1],RightBottomFront[2],1.0,0.0,normalBottom[0],normalBottom[1],normalBottom[2],
		LeftBottomFront[0],LeftBottomFront[1],LeftBottomFront[2],0.0,0.0,normalBottom[0],normalBottom[1],normalBottom[2],
		LeftBottomBack[0],LeftBottomBack[1],LeftBottomBack[2],0.0,1.0,normalBottom[0],normalBottom[1],normalBottom[2],
		RightBottomBack[0],RightBottomBack[1],RightBottomBack[2],1.0,1.0,normalBottom[0],normalBottom[1],normalBottom[2],
	};
	glBindBuffer(GL_ARRAY_BUFFER, vboId);
	glBufferData(GL_ARRAY_BUFFER, 8*36*sizeof(float), cubeVertices, GL_STATIC_DRAW);
	//-------------------------------
	int tWidth, tHeight, tBpp;
	char *textureData = LoadTGA("sdcard/texture2.tga",&tWidth, &tHeight, &tBpp);
	glGenTextures(1,&texID);
	glActiveTexture(GL_TEXTURE0);
	glBindTexture(GL_TEXTURE_2D,texID);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
	glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
	glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
	glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
	glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP, GL_TRUE); //Or gluBuild2DMipmaps(old)/glGenerateMipmap(new, android support)
	GLfloat largest_supported_anisotropy;
	glGetFloatv(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &largest_supported_anisotropy);
	glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, largest_supported_anisotropy);
	glTexImage2D(GL_TEXTURE_2D,0,GL_RGB,tWidth,tHeight,0,GL_RGB,GL_UNSIGNED_BYTE,textureData);
	delete[] textureData;
	glEnable(GL_DEPTH_TEST);
	glEnable(GL_CULL_FACE);
	glEnable(GL_MULTISAMPLE);
	camera.SetCameraLookAt(100.0f, 200.0f, 200.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f);
	f = fopen("sdcard/texture2.tga", "rb");
	if(f)
	{
		etcTest.LoadFromFile(f);
		fclose(f);
	}

	f = fopen("sdcard/room_vs.txt","rb");
	if(f)
	{
		LOGI("hieu.dotri open vs_room.txt");
		vs_room = glCreateShader(GL_VERTEX_SHADER);
		GLchar *shaderSrc;
		fseek(f,0,SEEK_END);
		GLint size = ftell(f);
		shaderSrc = new GLchar[size + 1];
		fseek(f,0,SEEK_SET);
		fread(shaderSrc,1,size,f);
		shaderSrc[size] = '\0';
		fclose(f);
		glShaderSource(vs_room,1,(const char **)&shaderSrc,0);
		glCompileShader(vs_room);
		char buffer[512];
		int length = 0;
		glGetShaderInfoLog(vs_room, 512, &length, buffer);
		if(length > 0)
		{
			LOGI("hieu.dotri2 vs_room error: %s", buffer);
		}
		delete[] shaderSrc;
	}
	else
	{
		LOGI("hieu.dotri can not open vs_room.txt");
	}
	f = fopen("sdcard/room_fs.txt","rb");
	if(f)
	{
		LOGI("hieu.dotri open fs_room.txt");
		fs_room = glCreateShader(GL_FRAGMENT_SHADER);
		GLchar *shaderSrc;
		fseek(f,0,SEEK_END);
		GLint size = ftell(f);
		shaderSrc = new GLchar[size + 1];
		fseek(f,0,SEEK_SET);
		fread(shaderSrc,1,size,f);
		shaderSrc[size] = '\0';
		fclose(f);
		glShaderSource(fs_room,1,(const char **)&shaderSrc,0);
		glCompileShader(fs_room);
		char buffer[512];
		int length = 0;
		glGetShaderInfoLog(fs_room, 512, &length, buffer);
		if(length > 0)
		{
			LOGI("hieu.dotri2 fs_room error: %s", buffer);
		}
		delete[] shaderSrc;
	}
	else
	{
		LOGI("hieu.dotri can not open fs_room.txt");
	}
	prg_room.id = glCreateProgram();
	glAttachShader(prg_room.id,vs_room);
	glAttachShader(prg_room.id,fs_room);
	glLinkProgram(prg_room.id);
	{
		char buffer[512];
		int length = 0;
		glGetProgramInfoLog(prg_room.id, 512, &length, buffer);
		if(length > 0)
		{
			LOGI("hieu.dotri2 prg_room error: %s", buffer);
		}
	}
	prg_room.mMatrix = glGetUniformLocation(prg_room.id,"u_mMatrix");
	prg_room.pMatrix = glGetUniformLocation(prg_room.id,"u_pMatrix");
	prg_room.vMatrix = glGetUniformLocation(prg_room.id,"u_vMatrix");
	prg_room.lightMatrix = glGetUniformLocation(prg_room.id,"u_lightMatrix");
	prg_room.lightPos = glGetUniformLocation(prg_room.id,"u_lightPos");
	prg_room.depthTexture = glGetUniformLocation(prg_room.id,"textureShadow");

	prg_room.position = glGetAttribLocation(prg_room.id,"a_posL");
	prg_room.normal = glGetAttribLocation(prg_room.id,"a_normal");

	fbo.Init(width, height);
	lightPointPosition.x = 40.0f;
	lightPointPosition.y = 80.0f;
	lightPointPosition.z = 40.0f;
	lightPointPosition.w = 1.0f;
}
void Game::Release()
{
	glDeleteTextures(1,&texID);
	glDeleteBuffers(1,&vboId);
	fbo.Release();
	if(instance) delete instance;
	instance = 0;
}
void Game::Update(long timeElapsed)
{
	Matrix rotate;
	Matrix translate;
	static float angle = 0.0;
	angle += 0.01;
	rotate.SetIdentity();
	rotate.SetRotateY(angle);
	translate.SetTranslate(0.0,-15.0,-55.0);
	Matrix::MatrixMultiply(&translate,&rotate,&mxModelView);
	static Vector4f temp = lightPointPosition;
	lightPointPosition = rotate * temp;
}
void Game::Render()
{
	
	glClearColor(0,0,0,0);
	glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
	//glEnable(GL_BLEND);
	//glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
	//DrawCircle(0,0,200,1,0,0);
	DrawShadow();
	DrawBox();
	DrawRoom();
}
int Game::GetScreenWidth()
{
	return iScreenWidth;
}
int Game::GetScreenHeight()
{
	return iScreenHeight;
}
void Game::DrawBox()
{
	glUseProgram(prg);
	

	//glUniformMatrix4fv(mvMatrix,1,GL_FALSE,mxModelView);
	glUniformMatrix4fv(mvMatrix,1,GL_FALSE,camera.GetViewMatrix());
	

	glUniformMatrix4fv(pMatrix,1,GL_FALSE,mxProjection);

	
	glUniform3f(uniformLightPosition,1.0,1.0,1.0);

	
	glUniform4f(uniformLightDiffuse,1.0,1.0,1.0,1.0);

	glUniform4f(uniformAmbient,0.4,0.4,0.4,1.0);

	glBindBuffer(GL_ARRAY_BUFFER, vboId);
	
	glVertexAttribPointer(posAttribute,3,GL_FLOAT,GL_FALSE,8*sizeof(GLfloat),0);
	glEnableVertexAttribArray(posAttribute);
	
	glVertexAttribPointer(normalAttribute,3,GL_FLOAT,GL_FALSE,8*sizeof(GLfloat),(void*)(5*sizeof(GLfloat)));
	glEnableVertexAttribArray(normalAttribute);

	glVertexAttribPointer(texCoordAttribute,2,GL_FLOAT,GL_FALSE,8*sizeof(GLfloat),(void*)(3*sizeof(GLfloat)));
	glEnableVertexAttribArray(texCoordAttribute);

	glUniform1i(uniformTexture,0);
	glActiveTexture(GL_TEXTURE0);
	glBindTexture(GL_TEXTURE_2D,etcTest.idGL);

	glDrawArrays(GL_TRIANGLES,0,36);
}
void Game::DrawRoom()
{
	glDisable(GL_CULL_FACE);
	glUseProgram(prg_room.id);
	static Matrix mMatrix;
	mMatrix.SetScaleUniform(5.0f);
	glUniformMatrix4fv(prg_room.mMatrix,1,GL_FALSE,mMatrix);
	glUniformMatrix4fv(prg_room.vMatrix,1,GL_FALSE,camera.GetViewMatrix());

	glUniformMatrix4fv(prg_room.pMatrix,1,GL_FALSE,mxProjection);

	glUniformMatrix4fv(prg_room.lightMatrix,1,GL_FALSE,lightPointSpaceMatrix);
	glUniform3f(prg_room.lightPos, lightPointPosition.x, lightPointPosition.y, lightPointPosition.z);


	glBindBuffer(GL_ARRAY_BUFFER, vboId);
	
	glVertexAttribPointer(prg_room.position,3,GL_FLOAT,GL_FALSE,8*sizeof(GLfloat),0);
	glEnableVertexAttribArray(prg_room.position);
	
	glVertexAttribPointer(prg_room.normal,3,GL_FLOAT,GL_FALSE,8*sizeof(GLfloat),(void*)(5*sizeof(GLfloat)));
	glEnableVertexAttribArray(prg_room.normal);
	
	glUniform1i(prg_room.depthTexture,0);
	glActiveTexture(GL_TEXTURE0);
	glBindTexture(GL_TEXTURE_2D,fbo.depthID);

	glDrawArrays(GL_TRIANGLES,12,12);
	glDrawArrays(GL_TRIANGLES,30,6);
	glEnable(GL_CULL_FACE);
}
void Game::DrawShadow()
{
	glBindFramebuffer(GL_FRAMEBUFFER, fbo.ID);
	glClearColor(0.0f,1.0f,0.0f,1.0f);
	glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
	glClearColor(0.0f,0.0f,0.0f,0.0f);
	Vector3f camP = camera.cameraPos;
	Vector3f camD = camera.direction;
	Vector3f camU = camera.up;
	camera.SetCameraLookAt(lightPointPosition.x, lightPointPosition.y, lightPointPosition.z, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f);
	Matrix::MatrixMultiply(&mxProjection, &(camera.GetViewMatrix()), &lightPointSpaceMatrix);
	DrawBox();
	glBindFramebuffer(GL_FRAMEBUFFER, 0);
	camera.cameraPos = camP;
	camera.direction = camD;
	camera.up = camU;
	camera.UpdateViewMatrix();
}
void Game::OnKeyDown(int key)
{
	switch(key)
	{
	case 0x41: //A
		camera.MoveLeft(0.3f);
		break;
	case 0x44: //D
		camera.MoveRight(0.3f);
		break;
	case 0x53: //S
		camera.MoveBackwardFPS(0.3f);
		break;
	case 0x57: //W
		camera.MoveForwardFPS(0.3f);
		break;
	case VK_LEFT: //Z
		camera.rotateOy(0.05f);
		break;
	case VK_RIGHT: //C
		camera.rotateOy(-0.05f);
		break;
	case VK_UP: //Z
		camera.rotateOx(0.05f);
		break;
	case VK_DOWN: //C
		camera.rotateOx(-0.05f);
		break;
	}
}